package com.syp.auth.service.base.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaBuilder.In;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.syp.auth.dao.po.ResourceEntity;
import com.syp.auth.dao.po.RoleEntity;
import com.syp.auth.dao.po.RoleResourceEntity;
import com.syp.auth.dao.repository.ResourceRepository;
import com.syp.auth.dao.repository.RoleRepository;
import com.syp.auth.dao.repository.RoleResourceRepository;
import com.syp.auth.dao.repository.UserRoleRepository;
import com.syp.auth.domain.vo.RoleVo;
import com.syp.auth.domain.vo.easyui.TreeNode;
import com.syp.auth.service.base.RoleServiceI;

@Service("roleService")
public class RoleServiceImpl implements RoleServiceI {

	@Autowired
	private RoleRepository roleRepository = null;
	@Autowired
	private UserRoleRepository userRoleRepository = null;
	@Autowired
	private RoleResourceRepository roleResourceRepository = null;
	@Autowired
	private ResourceRepository resourceRepository = null;

	@Override
	public void add(RoleVo role) {
		RoleEntity roleEntity = new RoleEntity();
		BeanUtils.copyProperties(role, roleEntity);
		roleEntity.setLastUpdate(Calendar.getInstance().getTime());
		roleEntity.setCreateTime(Calendar.getInstance().getTime());
		// 上级角色
		this.roleRepository.save(roleEntity);
	}

	public List<RoleEntity> children(final Long pid, final List<Long> limitIds) {
		Specification<RoleEntity> spec = new Specification<RoleEntity>() {
			@Override
			public Predicate toPredicate(Root<RoleEntity> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
				List<Predicate> pList = new ArrayList<>();
				if (limitIds != null && !limitIds.isEmpty()) {
					if (pid != null) {
						In<Long> inLimitIds = cb.in(root.get("id").as(Long.class));
						for (Long lid : limitIds) {
							inLimitIds.value(lid);
						}
						pList.add(inLimitIds);
						pList.add(cb.equal(root.get("rolePid"), pid));
					} else {
						In<Long> inLimitIds = cb.in(root.get("id").as(Long.class));
						for (Long lid : limitIds) {
							inLimitIds.value(lid);
						}
						pList.add(inLimitIds);
						In<Long> resPidimitIds = cb.in(root.get("rolePid").as(Long.class));
						for (Long lid : limitIds) {
							resPidimitIds.value(lid);
						}
						pList.add(cb.or(cb.isNull(root.get("rolePid")), resPidimitIds.not()));
					}

				} else {
					if (pid != null) {
						pList.add(cb.equal(root.get("rolePid"), pid));
					} else {
						pList.add(cb.isNull(root.get("rolePid")));
					}
				}
				return cb.and(pList.toArray(new Predicate[pList.size()]));
			}
		};
		Sort sort = new Sort(new Sort.Order(Sort.Direction.ASC, "roleSeq"), new Sort.Order(Sort.Direction.DESC, "id"));
		return this.roleRepository.findAll(spec, sort);
	}

	public long childrenCount(final Long pid, final List<Long> limitIds) {
		Specification<RoleEntity> spec = new Specification<RoleEntity>() {
			@Override
			public Predicate toPredicate(Root<RoleEntity> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
				List<Predicate> pList = new ArrayList<>();
				if (limitIds != null && !limitIds.isEmpty()) {
					if (pid != null) {
						In<Long> inLimitIds = cb.in(root.get("id").as(Long.class));
						for (Long lid : limitIds) {
							inLimitIds.value(lid);
						}
						pList.add(inLimitIds);
						pList.add(cb.equal(root.get("rolePid"), pid));
					} else {
						In<Long> inLimitIds = cb.in(root.get("id").as(Long.class));
						for (Long lid : limitIds) {
							inLimitIds.value(lid);
						}
						pList.add(inLimitIds);
						In<Long> resPidimitIds = cb.in(root.get("rolePid").as(Long.class));
						for (Long lid : limitIds) {
							resPidimitIds.value(lid);
						}
						pList.add(cb.or(cb.isNull(root.get("rolePid")), resPidimitIds.not()));
					}

				} else {
					if (pid != null) {
						pList.add(cb.equal(root.get("rolePid"), pid));
					} else {
						pList.add(cb.isNull(root.get("rolePid")));
					}
				}
				return cb.and(pList.toArray(new Predicate[pList.size()]));
			}
		};

		return this.roleRepository.count(spec);
	}

	@Transactional(rollbackOn = { RuntimeException.class })
	@Override
	public void delete(Long id) {
		List<RoleEntity> children = this.children(id, null);
		if (children != null && !children.isEmpty()) {
			for (RoleEntity rs : children) {
				this.delete(rs.getId());
			}
		}
		// 删除用户角色表对该角色的引用
		this.userRoleRepository.deleteByRoleId(id);
		// 删除角色资源表对该角色的引用
		this.roleResourceRepository.deleteByRoleId(id);
		// 删除该资源
		this.roleRepository.delete(id);
	}

	@Override
	public void edit(RoleVo role) {
		RoleEntity roleEntity = this.roleRepository.findOne(role.getId());
		BeanUtils.copyProperties(role, roleEntity);
		roleEntity.setLastUpdate(Calendar.getInstance().getTime());
		this.roleRepository.save(roleEntity);
	}

	@Override
	public RoleVo treegrid(RoleVo role, boolean recursive) {
		List<RoleEntity> list = this.children(role.getId(), null);
		if (!CollectionUtils.isEmpty(list)) {
			for (RoleEntity item : list) {
				RoleVo child = new RoleVo();

				BeanUtils.copyProperties(item, child);
				if (!StringUtils.isEmpty(item.getRolePid())) {
					RoleEntity parent = this.roleRepository.findOne(item.getRolePid());
					child.setRolePid(parent.getId());
					child.setRolePname(parent.getRoleName());
					child.setRolePDesc(parent.getRoleDesc());
				}

				List<RoleResourceEntity> roleResourceList = this.roleResourceRepository.getByRoleId(item.getId());
				List<Long> resIdsList = new ArrayList<Long>();
				List<String> resNamesList = new ArrayList<String>();
				if (!CollectionUtils.isEmpty(roleResourceList)) {
					for (RoleResourceEntity rrs : roleResourceList) {
						ResourceEntity resource = this.resourceRepository.findOne(rrs.getResId());
						resIdsList.add(resource.getId());
						resNamesList.add(resource.getResName());
					}
				}
				child.setResIds(StringUtils.collectionToCommaDelimitedString(resIdsList));
				child.setResNames(StringUtils.collectionToCommaDelimitedString(resNamesList));
				child.setIconCls("status_online");

				if (this.childrenCount(child.getId(), null) > 0) {
					child.setState("closed");
				} else {
					child.setState("open");
				}

				role.getChildren().add(child);
			}
		}
		return role;
	}

	@Override
	public TreeNode tree(RoleVo root, List<Long> limitIds, boolean recursive) {
		TreeNode tree = null; // 返回结果集

		// 模型转换
		tree = this.transTreeNode(root);
		if (tree == null) {
			tree = new TreeNode();
		}
		List<RoleEntity> list = this.children(root.getId(), limitIds);
		if (!CollectionUtils.isEmpty(list)) {
			for (RoleEntity item : list) {
				RoleVo child = new RoleVo();

				BeanUtils.copyProperties(item, child);
				if (!StringUtils.isEmpty(item.getRolePid())) {
					RoleEntity parent = this.roleRepository.findOne(item.getRolePid());
					child.setRolePid(parent.getId());
					child.setRolePname(parent.getRoleName());
					child.setRolePDesc(parent.getRoleDesc());
				}

				List<RoleResourceEntity> roleResourceList = this.roleResourceRepository.getByRoleId(item.getId());
				List<Long> resIdsList = new ArrayList<Long>();
				List<String> resNamesList = new ArrayList<String>();
				if (!CollectionUtils.isEmpty(roleResourceList)) {
					for (RoleResourceEntity rrs : roleResourceList) {
						ResourceEntity resource = this.resourceRepository.findOne(rrs.getResId());
						resIdsList.add(resource.getId());
						resNamesList.add(resource.getResName());
					}
				}
				child.setResIds(StringUtils.collectionToCommaDelimitedString(resIdsList));
				child.setResNames(StringUtils.collectionToCommaDelimitedString(resNamesList));
				child.setIconCls("status_online");

				if (this.childrenCount(child.getId(), null) > 0) {
					child.setState("closed");
				} else {
					child.setState("open");
				}
				TreeNode node = this.transTreeNode(child);
				if (recursive) {
					tree.getChildren().add(this.tree(child, limitIds, true));
				} else {
					tree.getChildren().add(node);
				}
			}
		}
		return tree;
	}

	@Transactional(rollbackOn = { RuntimeException.class })
	@Override
	public void grant(RoleVo role, List<Long> limitIds) {
		if (role.getResIds() != null) {
			// 删除原有权限
			if (limitIds != null && !limitIds.isEmpty()) {
				this.roleResourceRepository.deleteByRoleIdLimitedInMyDomain(role.getId(), limitIds);
			} else {
				this.roleResourceRepository.deleteByRoleId(role.getId());
			}
			// 添加新增权限
			if (!StringUtils.isEmpty(role.getResIds())) {
				List<RoleResourceEntity> roleResourceList = new LinkedList<>();
				for (String resId : role.getResIds().split(",")) {
					RoleResourceEntity rr = new RoleResourceEntity();
					rr.setRoleId(role.getId());
					rr.setResId(Long.valueOf(resId));
					roleResourceList.add(rr);
				}

				this.roleResourceRepository.save(roleResourceList);
			}
		}
	}

	@Override
	public RoleVo get(Long id) {
		RoleVo role = new RoleVo();
		RoleEntity roleEntity = this.roleRepository.findOne(id);
		BeanUtils.copyProperties(roleEntity, role);

		if (!StringUtils.isEmpty(roleEntity.getRolePid())) {
			RoleEntity parent = this.roleRepository.findOne(roleEntity.getRolePid());
			role.setRolePid(parent.getId());
			role.setRolePname(parent.getRoleName());
			role.setRolePDesc(parent.getRoleDesc());
		}

		List<RoleResourceEntity> roleResourceList = this.roleResourceRepository.getByRoleId(roleEntity.getId());
		List<Long> resIdsList = new ArrayList<Long>();
		List<String> resNamesList = new ArrayList<String>();
		if (!CollectionUtils.isEmpty(roleResourceList)) {
			for (RoleResourceEntity rrs : roleResourceList) {
				ResourceEntity resource = this.resourceRepository.findOne(rrs.getResId());
				resIdsList.add(resource.getId());
				resNamesList.add(resource.getResName());
			}
		}
		role.setResIds(StringUtils.collectionToCommaDelimitedString(resIdsList));
		role.setResNames(StringUtils.collectionToCommaDelimitedString(resNamesList));
		role.setIconCls("status_online");

		if (this.childrenCount(role.getId(), null) > 0) {
			role.setState("closed");
		} else {
			role.setState("open");
		}

		return role;
	}

	@Override
	public TreeNode transTreeNode(RoleVo role) {
		TreeNode node = null;
		RoleEntity roleEntity = new RoleEntity();
		if (role != null) {
			BeanUtils.copyProperties(role, roleEntity);
			node = new TreeNode();
			node.setId(role.getId());
			node.setText(role.getRoleName());
			Map<String, Object> attributes = new HashMap<String, Object>();
			node.setAttributes(attributes);

			node.setIconCls("status_online");
			if (this.childrenCount(role.getId(), null) > 0) {
				node.setState("closed");
			} else {
				node.setState("open");
			}
		}
		return node;
	}

	@Override
	public boolean checkRoleCodeUnique(String roleCode) {
		boolean unique = false;

		RoleEntity r = this.roleRepository.getByRoleCode(roleCode);
		if (r == null) {
			unique = true;
		}
		return unique;
	}
}
